package netty.rpc.client;

import com.alibaba.fastjson2.JSON;
import netty.rpc.client.config.ServiceProperties;
import netty.rpc.common.utils.SystemUtil;
import netty.rpc.core.*;
import netty.rpc.register.Register;
import netty.rpc.register.RegisterProperties;
import netty.rpc.register.RegisterService;
import netty.rpc.server.NettyRpcServer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.*;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.*;

/**
 * rpc接口初始化 创建动态代理
 * @author Jim
 * @version 1.0.0
 * @descripition:
 * @date 2022/5/15 13:55
 */
@Component
public class NettyRpcBootStrap implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

    private static final Logger logger = LogManager.getLogger(NettyRpcBootStrap.class);

    private ApplicationContext applicationContext;

    private ResourcePatternResolver resourcePatternResolver;

    private MetadataReaderFactory metadataReaderFactory;


    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent){
            start();
        }
    }

    private void start(){
        // 服务定义
        ServiceProperties properties = applicationContext.getBean(ServiceProperties.class);
        ServiceDefinition serviceDefinition = loadServiceDefinition(properties);
        logger.info("load serviceDefinition: {}",serviceDefinition);
        // 接口定义
        Map<String, Object> rpcInterfaceMap = applicationContext.getBeansWithAnnotation(NettyRpcInterface.class);
        for (String interfaceName:rpcInterfaceMap.keySet()){
            Object bean = rpcInterfaceMap.get(interfaceName);
            InterfaceDefinition interfaceDefinition = NettyRpcAnnotationScanner.getInstance().scan(bean,serviceDefinition);
            if (interfaceDefinition == null){
                continue;
            }
            logger.info("load interfaceDefinition:{}",interfaceDefinition);
            serviceDefinition.getInterfaceSet().add(interfaceDefinition);
        }
        //实例定义
        ServiceInstance serviceInstance = buildServiceInstance(serviceDefinition,properties);

        ServiceRegistry serviceRegistry = ServiceRegistry.getInstance();
        serviceRegistry.init(applicationContext.getBean(RegisterProperties.class));

        // 服务注册
        try {
            serviceRegistry.serviceRegister(serviceDefinition);
        } catch (Exception e) {
            logger.error("service register {} failure.",serviceDefinition.getServiceId(),e);
            System.exit(-1);
        }
        // 服务实例注册
        try {
            serviceRegistry.serviceInstanceRegister(serviceDefinition.getEnvType(),serviceInstance);
        } catch (Exception e) {
            logger.error("{} register failure.",serviceInstance.getServiceId(),e);
            System.exit(-1);
        }

        // 拉取服务列表
        try {
            serviceRegistry.pullServices(serviceDefinition.getEnvType());
        } catch (Exception e) {
            logger.error("pull registry failure.",serviceInstance.getServiceId(),e);
            System.exit(-1);
        }
        // 监听服务变化
        try {
            serviceRegistry.listenOnServiceChange(serviceDefinition.getEnvType());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 启动NettyServer
        NettyRpcServer nettyRpcServer = new NettyRpcServer(applicationContext);
        nettyRpcServer.start();
    }



    /**
     * 根据服务定义创建服务实例
     * @param serviceDefinition
     * @return
     */
    private ServiceInstance buildServiceInstance(ServiceDefinition serviceDefinition,ServiceProperties properties) {
        ServiceInstance serviceInstance = new ServiceInstance();
        serviceInstance.setServiceName(serviceDefinition.getServiceName());
        serviceInstance.setIp(SystemUtil.getLocalIp());
        serviceInstance.setPort(properties.getPort());
        serviceInstance.setServiceId(serviceDefinition.getServiceId());
        return serviceInstance;
    }





    /**
     * 加载配置 构造ServiceDefinition
     * @param properties
     * @return
     */
    private ServiceDefinition loadServiceDefinition(ServiceProperties properties) {
        ServiceDefinition serviceDefinition = loadApplicationConfig(properties);

        String mainClassName = applicationContext.getBeanNamesForAnnotation(EnableNettyRpc.class)[0];
        Object mainClass = applicationContext.getBean(mainClassName);
        EnableNettyRpc enableNettyRpc = mainClass.getClass().getAnnotation(EnableNettyRpc.class) == null ?
                mainClass.getClass().getSuperclass().getAnnotation(EnableNettyRpc.class):
                mainClass.getClass().getAnnotation(EnableNettyRpc.class);

        serviceDefinition.setEnable(enableNettyRpc.enable());
        serviceDefinition.setServiceId(serviceDefinition.getServiceName()+":"+serviceDefinition.getVersion());

        return serviceDefinition;
    }

    /**
     * 读取配置信息
     * @param properties
     */
    private ServiceDefinition loadApplicationConfig(ServiceProperties properties) {
        ServiceDefinition serviceDefinition = new ServiceDefinition();
        String activeProfile = applicationContext.getEnvironment().getActiveProfiles()[0];
        serviceDefinition.setServiceName(properties.getServiceName());
        serviceDefinition.setEnvType(activeProfile);
        serviceDefinition.setVersion(properties.getVersion());
        return serviceDefinition;
    }


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanDefinitionName:beanDefinitionNames){
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
            String className = beanDefinition.getBeanClassName();
            if (className == null){
                continue;
            }
            if (beanDefinition instanceof AnnotatedBeanDefinition){
                AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
                if (metadata.getAnnotationTypes().contains(SpringBootApplication.class.getName())){
                    String[] scanPaths = parseEnableNettyRpcScanPackage(metadata);
                    Set<Class<?>> allClasses = scan(scanPaths);
                    Set<Class<?>> rpcInterfaceClasses = filterRpcInterfaceClasses(allClasses);

                    // 核心步骤 对每个NettyRpc interface 创建bean描述
                    for (Class<?> rpcInterfaceClass: rpcInterfaceClasses){
                        // 生成Class对象的 描述对象
                        AbstractBeanDefinition proxyBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(rpcInterfaceClass).getRawBeanDefinition();
                        proxyBeanDefinition.setBeanClass(RpcServiceFactory.class);
                        proxyBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                        // 调用RpcServiceFactory 会传参rpcInterfaceClass
                        proxyBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(rpcInterfaceClass);
                        //注册到spring 容器中
                        registry.registerBeanDefinition(rpcInterfaceClass.getName(), proxyBeanDefinition);
                    }
                }
            }
        }
    }

    private Set<Class<?>> filterRpcInterfaceClasses(Set<Class<?>> allClasses) {
        Set<Class<?>> set = new HashSet<>();
        for (Class<?> clazz:allClasses){
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation:annotations){
                if (annotation instanceof NettyRpcInterface){
                    Object bean = null;
                    try {
                        bean = applicationContext.getBean(clazz);
                    } catch (BeansException e) {
                        logger.warn("{} bean not found.maybe it is an rpc interface",clazz);
                    }
                    if (bean == null){
                        set.add(clazz);
                    }
                }
            }
        }
        return set;
    }

    private Set<Class<?>> scan(String[] scanPaths) {
        Set<Class<?>> set = new LinkedHashSet<>();
        for (String path:scanPaths){
            String scanPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+resolveBasePackage(path)+"/**/*.class";
            //根据路径加载资源
            try {
                Resource[] resources = this.resourcePatternResolver.getResources(scanPath);
                for (Resource resource:resources){
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    Class<?> clazz = Class.forName(className);
                    set.add(clazz);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return set;
    }

    private Environment getEnvironment(){return applicationContext.getEnvironment();}


    private String resolveBasePackage(String path) {
        return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(path));
    }

    private String[] parseEnableNettyRpcScanPackage(AnnotationMetadata metadata) {
        Class<?> bootStrapClass = null;
        try {
            bootStrapClass = Class.forName(metadata.getClassName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Annotation[] annotations = bootStrapClass.getAnnotations();
        for (Annotation annotation:annotations){
            if (annotation instanceof EnableNettyRpc){
                EnableNettyRpc enableNettyRpc = (EnableNettyRpc) annotation;
                return enableNettyRpc.value();
            }
        }
        return null;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }


}
